#include "common.h"
#include "../util/poll_set.hpp"                                                                                                                                                           
#include "../util/poll_manager.hpp"
#include "../transport/transport_kcp.hpp"
#include "../util/util.hpp"
#include "../util/Trace.hpp"


//mmap
//#include <unistd.h>
//#include <sys/mman.h>

#include <string>
#include <string.h>
#include <sys/stat.h>//struct stat

#include <boost/make_shared.hpp>
#include <boost/smart_ptr.hpp>
#include <functional>
#include <assert.h>
#include <memory>
#include <unistd.h>



#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>        
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>    

#include <time.h>
 
#define handle_error(msg) \
	do { perror(msg); exit(EXIT_FAILURE); } while(0)

//get sizeof of file , Bytes
size_t getFileSize(const char *fileName)
{
  if(fileName == NULL)
    return 0;
  struct stat statbuf;
  stat(fileName, &statbuf);
  size_t filesize = statbuf.st_size;
  return filesize;
}

bool UDP_write(TransportUDPPtr transport, unsigned char* buffer, uint32_t size)
{


  buffer;
  uint32_t write_size = size;
  uint32_t write_sent = 0;
  bool has_write_callback = true;

  //writeUDPTransport();
  bool writing = true;
  bool can_write_more = true;


  transport->enableWrite();
  while(has_write_callback && can_write_more)
  {


    uint32_t to_write = write_size - write_sent;
    //ROS_DEBUG_NAMED("superdebug", "Connection Client Writing %d bytes", to_write);
    TRACE("[INFO] superdebug Connection Client Writing ", to_write, " bytes");
    uint32_t bytes_sent = transport->write(buffer+write_sent, to_write);
    //ROS_DEBUG_NAMED("superdebug", "Connection Client Wrote %d bytes", bytes_sent);
    TRACE("[INFO] superdebug Connection Client Wrote ", bytes_sent, " bytes");

                                                                                                                                                                                          
    if(bytes_sent<0)
    {
      writing = false;
      return false;
    }
    write_sent += bytes_sent;
                                                                                                                                                                                         
    TRACE("[INFO] superdebug Connection Client total Wrote ", write_sent, " bytes");

    if(bytes_sent < (int)write_size - (int)write_sent)
    {
      can_write_more = false;
    }

    if(write_sent == write_size)
    {
      //buffer.reset();
      write_sent = 0;
      write_size = 0;
      has_write_callback = 0;
    }
    //usleep(1);
  }

  if(!has_write_callback)
  {
    transport->disableWrite();
  }
//  buffer.reset();
  writing = false;

  return true;
}

void checkForShutdown()
{

}



int
main(int argc, const char *argv[]){

  
  /*-------------------------------------------------------------------------*/	
	FILE *fp;
	ssize_t send_size;
	char send_buf[1024+1]={0};
	//char send_buf[5120+1]={0};
	//char send_buf[10240+1]={0};
	int size_number=0;
  size_t file_size = 0;
	
	if(argc!=2){		
		fprintf(stderr,"Usage: %s <filename> \n",argv[0]);
		exit(EXIT_FAILURE);		
	}
	
	//if((fp = fopen(argv[1], "r+")) == NULL)
	if((fp = fopen(argv[1], "rb")) == NULL)
  { 
		handle_error("fopen");		
	}
  TRACE("file<", argv[1],  "> open successfully!\n");
  file_size = getFileSize(argv[1]); //Byte
	
  /*---------------------------------------------------------------------------*/
  /*---------------------------------------------------------------------------*/
  //TRACE("[INFO] start PollManager.. loading...");
  PollManager::instance()->addPollThreadListener(checkForShutdown);
  PollManager::instance()->start();
  //TRACE("[INFO] start PollManager.. ok...");
  

  TRACE("[INFO] create udp_transport...");
  PollSet poll_set;
  TransportUDPPtr udp_transport; 
  udp_transport = std::make_shared<TransportUDP>(&PollManager::instance()->getPollSet());
  TRACE("[INFO] ok!");



  //uint8_t tmp[20] = "Hello World:";
  //boost::shared_array<uint8_t> buffer(tmp);
  //unsigned char buffer[1024];

  //sleep(1);
  TRACE("[INFO] connect...");
  //if(!udp_transport->connect("82.157.129.44", 8080,  "172.17.109.9",9090, 300, false))
  if(!udp_transport->connect("127.0.0.1", 8080,  "127.0.0.1",9090, 300, false))
  {
    TRACE("[INFO] KCPClient connect failed");
    TRACE("[BREAK]");assert(0);//ROS_BREAK
  }
  TRACE("[INFO] connect successfully!");

  /*---------------------------------------------------------------------------*/


  /*---------------------------------------------------------------------------*/
  int clientfd;
  struct sockaddr_in serv_addr;
  memset(&serv_addr, 0, sizeof(serv_addr));
  if((clientfd = socket(AF_INET, SOCK_STREAM, 0))==-1)
  {
    std::cout<<"create socket failed : "<<strerror(errno)<<std::endl;
    return 0;
  }
  serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(6060);
	serv_addr.sin_addr.s_addr = inet_addr("127.0.0.1");
  if(connect(clientfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr))==-1)
  {
    std::cout<<"connect failed : "<<strerror(errno)<<std::endl;
    return 0;
  }

  char sdbuf[1024];
  char rvbuf[1024];
  int sendlen, recvlen = 0;
  sprintf(sdbuf, "Size:%ld Bytes", file_size);
  sendlen = write(clientfd, sdbuf, strlen(sdbuf));
  recvlen = read(clientfd, &rvbuf, sizeof(rvbuf));

  std::cout<<rvbuf<<std::endl;
  if(strncasecmp(rvbuf, "OK", 2)!=0)
  {
    std::cout<<"Send SIZE failed : "<<strerror(errno)<<std::endl;
    return 0;
  }

  //memset(sdbuf, 0, sizeof(sdbuf));

  shutdown(clientfd, SHUT_RDWR);
  close(clientfd);
  /*---------------------------------------------------------------------------*/

//#define CLOCK1
#define CLOCK2

#ifdef CLOCK1
  int begintime, endtime;
  begintime = clock();
#endif
  
  //time_t begintime, endtime;
  //begintime = time(NULL);
  
#ifdef CLOCK2
  int64_t begintime, endtime;
  begintime = iclock64();//ms
#endif
  
  /*---------------------------------------------------------------------------*/
  int recv_len = 0;
  //int send_cnt = 0;
  //memcpy(send_buf,"HELLO", sizeof("HELLO"));
  //recv_len = strlen(send_buf);
  //file_size = recv_len *1000*1000;
  /*---------------------------------------------------------------------------*/

	//while(NULL != fgets(send_buf, sizeof(send_buf)-1, fp)){
  //while(size_number<file_size)
  while((recv_len=fread(send_buf, sizeof(char), sizeof(send_buf)-1, fp))>=0 && size_number < file_size)
  //while((recv_len=fread(send_buf, sizeof(send_buf), 1, fp))>=0 && size_number < file_size)
  {
    //memcpy(send_buf,"HELLO", sizeof("HELLO"));
    //send_cnt++;

    if(recv_len==0)
    {
      //INFO("strlen(send_buf)=0", ", continue!");
      //return 0, means EOF of a file
      break;
    }

    //printf("|====================================|\n");
    //printf("after fread(), send_buf:\n");
    //printf("|====================================|\n");
    //printf("|::::::::::::::::::::::::::::::::::::|\n");
    //printf("%s\n", send_buf);
    //printf("|::::::::::::::::::::::::::::::::::::|\n");

		if(UDP_write(udp_transport, (unsigned char*)send_buf, recv_len)==false){
    //INFO("udp_transport->write()...");
    //if(udp_transport->write((unsigned char*)send_buf, recv_len)<0){
      //INFO("udp_transport->write()...wrong");
			fclose(fp); 
			handle_error("send");		
      break;
		}
    //INFO("udp_transport->write()...ok");
    
		size_number=size_number+recv_len;
    procBar(size_number, file_size);
		//usleep(1);
		
		bzero(send_buf, sizeof(send_buf));	
	}

#ifdef CLOCK1
  endtime = clock();
  double proc_time = (double)(endtime - begintime)/(double)CLOCKS_PER_SEC;//sec
#endif
  
  //endtime = time(NULL);
  //double proc_time = (double)difftime(endtime , begintime);//sec
  
#ifdef CLOCK2
  endtime = iclock64();
  double proc_time = (double)(endtime - begintime)/1000.0;//m sec
#endif

  double size_Mb = (((double)file_size*8)/(1024.0*1024.0));//Mbit
  double size_MB = (((double)file_size)/(1024.0*1024.0));//Mbit
  double rate = size_Mb/proc_time;//Mbit/s
  printf("|:::::::::::::::::::::::::::|\n");
  printf("file size: %lf Mb\n", size_Mb);
  printf("file size: %lf MB\n", size_MB);
  printf("use time: %lf s\n",proc_time);
  printf("rate: %lf Mbps\n", rate );
  printf("|:::::::::::::::::::::::::::|\n");

  printf("enter while(1)...\n");

  /*--------------------------------------------------------------------------------------*/

  while(1){usleep(1);}

  TRACE("[INFO] shutdown PollManager...loading...");

  PollManager::instance()->shutdown();

  TRACE("[INFO] shutdown PollManager...ok!");

	
	fclose(fp);

  TRACE("[INFO] fclose(fp)...ok!");
	
	return 0;
}
